/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦，本文采用木兰宽松许可证第2版]
 * 
 * https://zhiqim.org/project/zhiqim_kit/zhiqim_git.htm
 *
 * ZhiqimGit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.git.command;

import org.zhiqim.git.Git;
import org.zhiqim.git.GitCommand;
import org.zhiqim.git.GitWalker;
import org.zhiqim.git.objer.ObjectWriter;
import org.zhiqim.git.objer.model.PersonIdent;
import org.zhiqim.git.objer.model.Tag;
import org.zhiqim.git.objer.objectid.ObjectId;
import org.zhiqim.git.objer.objectid.RevCommit;
import org.zhiqim.git.objer.objectid.RevObject;
import org.zhiqim.git.refer.Ref;
import org.zhiqim.git.refer.RefUpdateResult;
import org.zhiqim.git.refer.RefUpdater;
import org.zhiqim.git.util.GitValidates;

import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Validates;

/**
 * 标签添加&修改命令
 * http://www.kernel.org/pub/software/scm/git/docs/git-tag.html
 * 
 * 标签有两种：
 * 1、简单标签，只对该版本作一个标记
 * 2、附注标签，作标记，同时提供标记注释和提交者信息
 * 
 * @version v1.0.0 @author zhichenggang 2017-3-25 新建与整理
 */
public class TagCommand extends GitCommand<Ref>
{
    private RevObject id;
    private String name;
    private String message;
    private PersonIdent tagger;
    private boolean forceUpdate;
    private boolean annotated = true;

    public TagCommand(Git git)
    {
        super(git);
    }
    
    /******************************************************************************/
    //设置参数
    /******************************************************************************/
    
    public TagCommand setName(String name)
    {
        this.name = name;
        return this;
    }

    public TagCommand setMessage(String message)
    {
        this.message = message;
        return this;
    }

    public TagCommand setTagger(PersonIdent tagger)
    {
        this.tagger = tagger;
        return this;
    }
    
    public TagCommand setForceUpdate(boolean forceUpdate)
    {
        this.forceUpdate = forceUpdate;
        return this;
    }

    public TagCommand setAnnotated(boolean annotated)
    {
        this.annotated = annotated;
        return this;
    }
    
    public TagCommand setObjectId(RevObject id)
    {
        this.id = id;
        return this;
    }

    /******************************************************************************/
    //检查&调用
    /******************************************************************************/
    
    protected void preprocess()
    {
        if (annotated)
        {//附注标签，如果消息和提交者未提供即默认一个
            if (tagger == null)
                tagger = new PersonIdent(git);
            
            if (Validates.isEmpty(message))
                message = "发布"+ name+"版本";
        }
        
        Asserts.assertNotEmptyBlank(name, "创建或更新标签时，标签名称不能为空白");
        Asserts.asserts(GitValidates.isValidRefName(P_REFS_TAGS_ + name), "创建或更新标签时，标签名称不合法");
    }
    
    protected Ref execute() throws Exception
    {
        GitWalker walker = new GitWalker(git);
        
        if (id == null)
        {//没有传入编号，缺省取HEAD
            id = git.resolve(HEAD, RevCommit.class);
        }

        if (!annotated)
        {
            if (message != null || tagger != null)
                throw new Exception("无注释标签不支持设置消息和提交信息");
            
            return updateTagRef(id, walker, name, "简单标签[" + name + " : " + id + "]"); 
        }

        Tag tag = new Tag();
        tag.setObject(id);
        tag.setType(RevObject.type(id.getType()));
        tag.setTag(name);
        tag.setTagger(tagger);
        tag.setMessage(message);
        
        //写入标签对象得到当前提交的对象编号
        ObjectWriter writer = new ObjectWriter(git);
        ObjectId tagId = writer.writeTag(tag);
        
        return updateTagRef(tagId, walker, name, tag.toString());
    }

    /**
     * 标签更新到引用文件中
     * 
     * @param tagId         标签标记的对象编号
     * @param walk          步行器
     * @param tagName       标签名称
     * @param error         错误提示
     * @return              成功得到引用
     * @throws Exception    异常
     */
    private Ref updateTagRef(ObjectId tagId, GitWalker walk, String tagName, String error) throws Exception
    {
        String refName = P_REFS_TAGS_ + tagName;
        
        RefUpdater tagRef = git.newUpdater(refName);
        tagRef.setNewObjectId(tagId);
        tagRef.setForceUpdate(forceUpdate);
        tagRef.setRefLogMessage("tagged " + name, false); 
        
        RefUpdateResult updateResult = tagRef.update(walk);
        switch (updateResult)
        {
        case NEW:
        case FORCED:
            return git.scanRef(refName);
        case LOCK_FAILURE:
            throw new Exception("锁定HEAD时失败：" + error);
        case REJECTED:
            throw new Exception("标签已存在：" + error);
        default:
            throw new Exception("更新引用失败：" + error);
        }
    }
}
